﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Elsa.Extensions;
using Elsa.JavaScript.Models;
using Elsa.Workflows;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Activities.Flowchart.Activities;
using Elsa.Workflows.Activities.Flowchart.Models;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
using Microsoft.CodeAnalysis;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using T.FAS.Runtime.MF.Activities;
using T.FAS.Runtime.MF.Activities.Helper;
using T.FAS.Runtime.MF.Entity;
using T.FAS.Runtime.MF.SPI;
using ElsaExpression = Elsa.Expressions.Models.Expression;

namespace T.FAS.Runtime.MF.Activities.Nodes
{
    internal class IterationNode : NodeBase, INodeMapper
    {
        [JsonProperty("data")]
        public new IterationNodeData Data { get; set; }
        [JsonProperty("children")]
        public new List<string> Children { get; set; }
        public List<Variable> Variables { get; set; } = new List<Variable>();
        public List<OutputDefinition> Outputs { get; set; }
        public List<InputDefinition> Inputs { get; set; } = new List<InputDefinition>();
        public override List<UserVariable> NodeInputOutputVariables
        {
            get
            {
                if (Data.OutputVariables == null) return new List<UserVariable>();
                Data.OutputVariables.ForEach(v =>
                {
                    SetVariableInfo(v);
                });
                return Data.OutputVariables;
            }
        }
        private void SetVariableInfo(UserVariable v)
        {
            if (string.IsNullOrEmpty(v.ActivityID))
            {
                v.ActivityID = Id;
                v.CompositeVarName = $"{Id}:{v.VariableName}";
                v.CompositeVarName = v.CompositeVarName.Replace("-", "").Replace(":", "");
            }
        }
        public IActivity MapToActivity()
        {
            if (Data.IsParallel)
            {
                return MapToParallelForeach();
            }
            else
            {
                return MapToForeach();
            }
        }

       private IActivity MapToForeach()
        {

            var activity = new ForEach() { Id = Id, Name = Data.Label };
       
            activity.Body = ConvertBody();
            activity.Metadata = DesignerMeta;

            //入参
            ConvertForeachInputVariable(activity);

            //出参
            var outputVariables = ConvertOutputVariables();
            //activity.OutputVariables = outputVariables;
            return activity;
        }

        private IActivity MapToParallelForeach()
        {
            var activity = new ParallelForEach() { Id = Id, Name = Data.Label };

            activity.Body = ConvertBody();
            activity.Metadata = DesignerMeta;

            //入参
            ConvertParallelForeachInputVariable(activity);

            //出参
           var outputVariables= ConvertOutputVariables();
            //activity.OutputVariables = outputVariables;
            return activity;
        }
        private Flowchart ConvertBody()
        {
            var activities = new List<IActivity>();
            var connections = new List<Elsa.Workflows.Activities.Flowchart.Models.Connection>();
            var activity = new ForEach() { Id = Id, Name = Data.Label };

            var childrenNodes = this.BelongTo.DesignData.Cells.Where(t => t.Parent == this.Id);
            var body = new Flowchart();
            //添加子节点
            foreach (var node in childrenNodes)
            {
                if (node is INodeMapper mapper)
                {
                    activities.Add(mapper.MapToActivity());
                }

            }
            body.Activities = activities;
            var ports = this.BelongTo.DesignData.Cells.Where(t => t.Ports != null).SelectMany(t => t.Ports.Items);
            //添加connections
            foreach (var cell in this.BelongTo.DesignData.Cells)
            {
                if (cell is Edge edge)
                {
                    if (!Children.Contains(edge.Source.Cell) && !Children.Contains(edge.Target.Cell)) continue;

                    var sourceActivity = activities.Find(t => t.Id == edge.Source.Cell);
                    var targetActivity = activities.Find(t => t.Id == edge.Target.Cell);
                    connections.Add(new Connection
                    {
                        Source = new Endpoint { Activity = sourceActivity, Port = ActivityHelper.GetActivityPort(sourceActivity, ports.FirstOrDefault(t => t.Id == edge.Source.Port)) },
                        Target = new Endpoint { Activity = targetActivity, Port = ActivityHelper.GetActivityPort(targetActivity, ports.FirstOrDefault(t => t.Id == edge.Target.Port)) }
                    });
                }
            }
            body.Connections = connections;
           
            return body;
        }
        private Dictionary<string, Variable> ConvertOutputVariables()
        {
            var activityOutputs = new Dictionary<string, Variable>();
            if (Data.OutputVariables!=null && Data.OutputVariables.Count > 0)
            {
                ReplaceAllVariableName(Data.OutputVariables);

                var outVariable = Data.OutputVariables[0];
                if (!string.IsNullOrEmpty(outVariable.VariableName))
                {
                    SetVariableInfo(outVariable);
                    var variable = VariableHelper.GenerateVariable(outVariable);
                    Variables.Add(variable);
                    activityOutputs.Add(outVariable.VariableName, variable);
                }
             
            }
            return activityOutputs;
        }
        private void ConvertParallelForeachInputVariable(ParallelForEach activity)
        {
            if (Data.InputVariables!=null && Data.InputVariables.Count > 0)
            {
                var inputVariable = Data.InputVariables[0];
                // var varInfo = (this.BelongTo.NodeInputOutputVariables ?? new List<UserVariable>()).Find(t => t.VariableName == inputVariable.VariableName && t.ActivityID == inputVariable.NodeID);
                var varInfo = ActivityHelper.GetSourceVariable(this, inputVariable.VariableName, inputVariable.NodeID);
                if (varInfo != null)
                {
                    //var variableExpression = new VariableExpression("myListVariable");
                    activity.Items = new Input<object>(JavaScriptExpression.Create($"variables.{varInfo.CompositeVarName}"));
                    //  activity.Items = new Input<ICollection<object>>(new Variable());
                }
            }
        }
        private void ConvertForeachInputVariable(ForEach activity)
        {
            if (Data.InputVariables != null && Data.InputVariables.Count > 0)
            {
                var inputVariable = Data.InputVariables[0];
                // var varInfo = (this.BelongTo.NodeInputOutputVariables ?? new List<UserVariable>()).Find(t => t.VariableName == inputVariable.VariableName && t.ActivityID == inputVariable.NodeID);
                var varInfo = ActivityHelper.GetSourceVariable(this, inputVariable.VariableName, inputVariable.NodeID);
                if (varInfo != null)
                {
                    //var variableExpression = new VariableExpression("myListVariable");
                    activity.Items = new Input<ICollection<object>>(JavaScriptExpression.Create($"variables.{varInfo.CompositeVarName}"));
                    //  activity.Items = new Input<ICollection<object>>(new Variable());
                }
            }
        }
        private void ReplaceAllVariableName(List<UserVariable> outputVariables)
        {
            foreach (var variable in outputVariables)
            {
                if (string.IsNullOrEmpty(variable.ValueType)) continue;
                variable.VariableValue = ActivityHelper.GetSourceVariableRealName(this, variable.VariableValue, variable.VariableNodeID);
                /*
                var varSelectors = variable.VariableValue.Split('.');
                var varItem = varSelectors[0];

                //从整个工作流中获取应的变量信息
                var varInfo = (this.BelongTo.NodeInputOutputVariables ?? new List<UserVariable>()).Find(t => t.VariableName == varItem && t.ActivityID == variable.VariableNodeID);
                if (varInfo == null) { continue; }
                var varName = varInfo.CompositeVarName;
                varSelectors[0] = varName;
                variable.VariableValue = string.Join(".", varSelectors);
                */
            }
        }
    }

    public class IterationNodeData
    {
        [JsonProperty("description")]
        public string Description { get; set; }

        [JsonProperty("label")]
        public string Label { get; set; }
        [JsonProperty("isParallel")]

        public bool IsParallel { get; set; }
        [JsonProperty("parallelNums")]

        public int ParallelNums { get; set; }
        [JsonProperty("status")]
        public string Status { get; set; }
        [JsonProperty("inputFields")]
        public List<UserVariable> InputVariables { get; set; }

        [JsonProperty("outputFields")]
        public List<UserVariable> OutputVariables { get; set; }
    }
}
